// 统一初始化数据库实例（根据业务需求选择：普通实例/ JQL实例）
// 若需多表关联查询，可替换为 uniCloud.databaseForJQL() 并配置权限
const db = uniCloud.database();
const _ = db.command;
const dbCmd = db.command
const redis = uniCloud.redis()
const amap = uniCloud.importObject('uni-map-co')
const linliUtils = require('linliUtils')
// 全局工具函数：校验 MongoDB ObjectId 格式
const isvalidObjectId = (id) => {
	return typeof id === 'string' && /^[0-9a-fA-F]{24}$/.test(id);
};

// 全局工具函数：校验经纬度合法性（范围+数字类型）
const isvalidLocation = (location) => {
	if (!location || typeof location !== 'object') return false;
	const {
		longitude,
		latitude
	} = location;
	if (typeof longitude !== 'number' || typeof latitude !== 'number') return false;
	return longitude >= -180 && longitude <= 180 && latitude >= -90 && latitude <= 90;
};

module.exports = {
	_before: function() {},

	/**
	 * 生成多边形样式配置
	 * @param {Array} polygon - 多边形坐标数组（[[lon1, lat1], [lon2, lat2], ...]）
	 * @returns {Object} 多边形样式
	 */
	getPolygon(polygon) {
		if (!Array.isArray(polygon) || polygon.length < 3) {
			console.error('无效的多边形坐标：需至少3个顶点');
			return {
				points: [],
				strokeWidth: 2,
				strokeColor: '#55aa7f',
				fillColor: 'rgba(0, 0, 0, 0.1)'
			};
		}

		const points = polygon.map(([longitude, latitude]) => ({
			longitude: Number(longitude) || 0,
			latitude: Number(latitude) || 0
		}));

		return {
			points,
			strokeWidth: 2,
			strokeColor: '#55aa7f',
			fillColor: 'rgba(0, 0, 0, 0.1)'
		};
	},
	/**
	 * @param {Object} userId 用户Id
	 * @param {Object} location 位置（含 longitude/latitude）
	 * @param {Object} type POI类型（如 'lastOnline'）
	 * @returns {Object} code 为 true 表示成功
	 */
	async setLocationToPoi(userId, location, type = 'lastOnline') {
		try {
			if (!userId || typeof userId !== 'string') {
				return {
					code: false,
					errMsg: '无效参数：userId 必须为有效字符串'
				};
			}
			// 校验 location（必须包含经纬度，且为数字类型）
			// 2. 构建符合表结构的写入数据（包含所有必填字段）
			const poiData = {
				userId: userId, // 表结构支持，关联 linli-user
				type: type, // 表结构支持，POI类型
				grid: linliUtils.getGridCode(location), // 表结构支持，格子编码

				location: location,

				title: userId,

				updateTime: Date.now(), // 更新时间（数字类型）

			};

			// 3. 执行 upsert 操作（无则新增，有则更新）
			const result = await db.collection('opendb-poi')
				.where({
					userId: userId, // 按用户筛选
					type: type // 按类型筛选（确保同一用户同一类型唯一）
				}).get()
			if (result.data.length > 0) {
				const updateRes = await db.collection('opendb-poi').where({
					userId: userId,
					type: type

				}).update(poiData)
				if (updateRes.updated > 0) {
					return {
						code: true,
						errMsg: `更新成功，影响 ${result.updated} 条记录`,
						data: poiData
					};
				} else {
					return {
						code: false,
						errMsg: "更新失败"
					}

				}



			} else {
				const addresult = await db.collection('opendb-poi').add(poiData)
				if (addresult.id) {
					return {
						code: true,
						errMsg: `新增成功，记录ID：${result.insertedId}`,
						data: poiData
					}
				} else {
					return {
						code: false,
						errMsg: "新增失败"
					}
				}
			}
		} catch (e) {
			console.error('setLocationToPoi 执行错误：', e);
			return {
				code: false,
				errMsg: `位置操作失败：${e.message || '未知错误'}`,



			};
		}
	},


	//数据库获取用户信息
	/**
	 * 初始化用户，从数据库获取用户信息，如果不存在则创建新用户
	 * @param {string} userTel - 用户手机号
	 * @returns {Promise<{user: object}|false>} 返回用户对象或false
	 */
	async getUserFromDb(userTel, pushId) {
		try {
			// 1. 按手机号查询用户（核心：仅匹配手机号，不提前过滤pushId）
			const res = await db.collection('linli-user').where({
				userTel: userTel
			}).get();

			// 2. 已找到该手机号对应的用户
			if (res.data && res.data.length > 0) {
				const user = res.data[0];

				// 校验核心字段：userId必须存在（需求前提）
				if (!user.userId) {
					throw new Error('用户信息不完整（缺少userId）');
				}

				// 情况1：有userId且有pushId → 直接返回用户信息
				if (user.push_clientid) {
					return {
						user
					};
				}

				// 情况2：有userId但无pushId → 更新pushId后返回
				const userData = {

					stateConfig: {
						autoPlayVoice: true,
						receiveDistance: 500,
						isSound: 1,
						acceptStrangerMsg: true,
						exposePhone: false,
						exposeComment: true,
						joinCluster: false,
						radius: 5
					},
					push_clientid: pushId,
					nicPic: {
						url: '',
						fileID: ''
					},
					joinedGroups: [], // 改为更合适的命名
					gridRadius: 5,
					signature: '', // 统一为小写
					favorite: [],
					updateTime: Date.now()
				};


				const updateResult = await db.collection('linli-user').doc(user._id).update({
					...userData,

				});

				// 校验更新结果（确保pushId更新成功）
				if (updateResult.updated === 0) {
					throw new Error('更新用户pushId失败（无数据变更）');
				}

				// 返回更新后的用户信息（直接拼接，减少一次数据库查询）
				const updatedUser = {
					...user,
					push_clientid: pushId
				};
				return {
					user: updatedUser
				};
			}

			// 3. 未找到用户（无userId且无pushId）→ 创建新用户
			const registResult = await registUser(userTel, pushId);
			// 校验注册接口返回
			if (registResult.code !== 0 || !registResult.data) {
				throw new Error('注册新用户失败：' + (registResult.msg || '未知错误'));
			}

			// 组装新用户数据（补充必要字段）
			const newUserData = {
				...registResult.data,
				userTel: userTel,
				push_clientid: pushId,
				registerTime: Date.now(), // 可选：创建时间
				updateTime: Date.now() // 可选：更新时间
			};

			// 添加到数据库
			const addResult = await db.collection('linli-user').add(newUserData);
			console.log("添加到。。。。。", addResult);
			if (!addResult.id) {
				throw new Error('新用户添加到数据库失败');
			}

			// 查询并返回新用户（确保数据完整性）
			const newUserRes = await db.collection('linli-user').doc(addResult.id).get();
			console.log("注册后的。。。。。", newUserRes);
			if (!newUserRes.data) {
				throw new Error('未查询到新创建的用户');
			}
			const newUser = newUserRes.data[0];

			// 校验新用户userId（避免注册接口返回异常）
			if (!newUser.userId) {
				throw new Error('新用户信息不完整（缺少userId）');
			}

			return {
				user: newUser
			};

		} catch (e) {
			console.error('获取用户信息失败:', e);
			throw new Error('获取用户信息失败：' + e.message);
		}
	},

	/**
	 * 根据位置定位所属群（网格预筛选 + 点在多边形校验）
	 * @param {Object} location - 目标位置 { longitude: number, latitude: number }
	 * @returns {Object} 统一格式结果 { code: boolean, msg: string, data: Object|null }
	 */
	async getGroup(location) {
		try {
			console.log('地理位置', location);
			// 1. 校验位置参数
			if (!isvalidLocation(location)) {
				return {
					code: false,
					msg: '无效的位置参数：经纬度需为数字且在合法范围'
				};
			}

			// 2. 生成网格编码与周边网格（添加保护）
			const centerGrid = linliUtils.getGridCode(location);
			console.log('中心点', centerGrid);
			// 验证生成的网格码是否有效
			if (centerGrid === '0.000_0.000') {
				return {
					code: false,
					msg: '位置参数处理失败'
				};
			}

			const radius = 3; // 进一步缩小半径到3
			const grids = linliUtils.getSurroundingGrids(centerGrid, radius);

			// 如果网格数组为空或过大，直接返回
			if (!grids.length || grids.length > 50) {
				console.warn('网格数量异常：', grids.length);
				return {
					code: true,
					msg: '周边群查询条件异常',
					data: null
				};
			}

			// 3. 预筛选周边网格内的群
			const groupsRes = await db.collection('Group').where({
				gridCode: _.in(grids)
			}).get();
			if (groupsRes.data.length > 0) {
				return {
					code: true,
					data: groupsRes.data[0]
				}
			} else {
				return {
					code: false,
					msg: '附近无管家服务'
				}
			}

		} catch (error) {
			console.error('getGroup 执行错误：', {
				type: error.name,
				message: error.message,
				stack: error.stack
			});
			return {
				code: false,
				msg: '群定位失败，请稍后重试',
				error: process.env.NODE_ENV === 'development' ? error.message : undefined
			};
		}
	},
	/**
	 * 查询指定位置周边的消息数量
	 * @param {Object} location - 位置对象，需包含 latitude（纬度）和 longitude（经度）
	 * @param {Number} [lastTime] - 时间阈值（毫秒时间戳），默认查近72小时
	 * @returns {Promise<Object>} - { code: 布尔值, count: 消息数量, msg: 错误信息 }
	 */
	async getMsgCount(location, lastTime) {
		try {
			// 1. 参数校验：确保location有效（必须包含经纬度）
			if (!location || !location.latitude || !location.longitude) {
				return {
					code: false,
					msg: "位置参数无效，需包含 latitude（纬度）和 longitude（经度）"
				};
			}

			// 2. 网格计算：捕获网格生成可能的错误（如经纬度超出合理范围）
			let centerGrid;
			let aroundGrids;
			try {
				centerGrid = linliUtils.getGridCode(location);
				// 校验中心网格有效性（假设linliUtils.getGridCode返回非空字符串/数字，具体规则需匹配实际逻辑）
				if (!centerGrid || (typeof centerGrid !== "string" && typeof centerGrid !== "number")) {
					return {
						code: false,
						msg: "生成中心网格失败，位置经纬度可能超出有效范围"
					};
				}
				aroundGrids = linliUtils.getSurroundingGrids(centerGrid, 5);
				// 校验周边网格非空（避免后续in查询传空数组导致全表扫描）
				if (!Array.isArray(aroundGrids) || aroundGrids.length === 0) {
					return {
						code: false,
						msg: "生成周边网格失败，无有效网格数据"
					};
				}
			} catch (gridErr) {
				return {
					code: false,
					msg: `网格计算错误：${gridErr.message || "未知错误"}`
				};
			}

			// 3. 时间处理：确保lastTime为有效时间戳（避免传非数字导致查询异常）
			const currentTime = new Date().getTime();
			let lastTime72;
			if (lastTime) {
				// 若传lastTime，校验是否为有效时间戳（且不大于当前时间）
				if (typeof lastTime !== "number" || lastTime < 0 || lastTime > currentTime) {
					return {
						code: false,
						msg: "时间阈值无效，需为0到当前时间的毫秒时间戳"
					};
				}
				lastTime72 = lastTime;
			} else {
				// 默认查近72小时（避免时间计算溢出，用Math.max确保非负）
				lastTime72 = Math.max(0, currentTime - 72 * 3600 * 1000);
			}

			// 4. 数据库查询：增加查询超时处理（避免长期阻塞）+ 结果校验
			const res = await db.collection('message_public')
				.where({
					grid: _.in(aroundGrids), // 用in查询周边网格，需确保aroundGrids非空（已前置校验）
					timestamp: _.gt(lastTime72) // 只查时间阈值之后的消息
				})
				.orderBy('timestamp', 'desc')
				.count()
				.catch(dbErr => {
					// 捕获数据库查询特定错误（如权限不足、集合不存在）
					throw new Error(`数据库查询失败：${dbErr.message || "未知数据库错误"}`);
				});
			console.log("======", res);
			// 5. 结果返回：校验count有效性（确保为数字）
			const msgCount = typeof res.total === "number" ? res.total : 0;
			return {
				code: true,
				count: msgCount,
				msg: "消息数量查询成功"
			};

		} catch (e) {
			// 全局异常捕获：统一错误格式，便于调用方处理
			console.error("getMsgCount 全局异常：", e);
			return {
				code: false,
				msg: `查询消息数量失败：${e.message || "未知异常"}`,
				count: 0 // 错误时返回count=0，避免调用方解构报错
			};
		}
	},
	/**
	 * @param {String} userId 用户ID
	 * @param {Object} config 设置对象
	 */
	async saveConfig(userId, config) {

		try {
			const updata = await db
				.collection('linli-user')
				.where({
					userId: userId
				})
				.update({
					stateConfig: config
				});
			if (updata.updated > 0) {
				return true
			} else {
				return false
			}
		} catch (e) {
			return false
		}
	},

	/**
	 * 评论管理（增删查）
	 * @param {Object} params - 参数对象
	 * @param {string} params.sendUserId - 操作用户ID（自动从登录态获取，前端传参无效）
	 * @param {string} params.content - 评论内容（add时必传）
	 * @param {string} params.publicMsgId - 公共消息ID（add/get时必传）
	 * @param {string} params.type - 操作类型（add/del/get）
	 * @param {string} params.id - 评论ID（del时必传）
	 * @returns {Object} 统一格式结果
	 */
	async commentMsg(params) {
		try {
			const {
				content,
				publicMsgId,
				type,
				sendUserId
			} = params;


			// 1. 基础类型校验
			if (!['add', 'del', 'get'].includes(type)) {
				return {
					code: false,
					msg: `不支持的操作类型：${type}，仅支持 add/del/get`
				};
			}

			// 2. 分支逻辑处理
			// 2.1 新增评论
			if (type === 'add') {
				if (!publicMsgId || !isvalidObjectId(publicMsgId)) {
					return {
						code: false,
						msg: '无效的公共消息ID'
					};
				}
				if (typeof content !== 'string' || content.trim().length === 0) {
					return {
						code: false,
						msg: '评论内容不能为空'
					};
				}

				const addRes = await db.collection('comment_content').add({
					sendUserId,
					content: content.trim(),
					publicMsgId,
					createTime: Date.now(),
					isDeleted: false // 新增：软删除标记，便于数据恢复
				});

				return addRes.id ? {
					code: true,
					msg: '评论添加成功',
					data: {
						commentId: addRes.id
					}
				} : {
					code: false,
					msg: '评论添加失败，请稍后重试'
				};
			}

			// 2.2 删除评论（校验作者权限）
			if (type === 'del') {
				if (!id || !isvalidObjectId(id)) {
					return {
						code: false,
						msg: '无效的评论ID'
					};
				}

				// 查询评论详情，校验作者身份
				const commentRes = await db.collection('comment_content').where({
					_id: id,
					isDeleted: false
				}).get();

				if (commentRes.data.length === 0) {
					return {
						code: false,
						msg: '评论不存在或已删除'
					};
				}

				const comment = commentRes.data[0];
				if (comment.sendUserId !== sendUserId) {
					return {
						code: false,
						msg: '无权限删除他人评论'
					};
				}

				// 软删除（推荐：避免误删后无法恢复）
				const delRes = await db.collection('comment_content').where({
					_id: id
				}).update({
					isDeleted: true,
					deleteTime: Date.now()
				});

				return delRes.updated > 0 ? {
					code: true,
					msg: '评论删除成功'
				} : {
					code: false,
					msg: '评论删除失败，请稍后重试'
				};
			}

			// 2.3 获取评论列表
			if (type === 'get') {
				if (!publicMsgId || !isvalidObjectId(publicMsgId)) {
					return {
						code: false,
						msg: '无效的公共消息ID'
					};
				}

				const listRes = await db.collection('comment_content')
					.where({
						publicMsgId,
						isDeleted: false
					})
					.field({
						isDeleted: 0,
						deleteTime: 0
					}) // 隐藏内部字段
					.orderBy('createTime', 'desc')
					.get();

				return {
					code: true,
					msg: '评论列表获取成功',
					data: listRes.data || []
				};
			}

			// 兜底：未匹配的类型（理论上不会触发）
			return {
				code: false,
				msg: '操作类型错误'
			};
		} catch (error) {
			console.error('commentMsg 执行错误：', error);
			return {
				code: false,
				msg: '评论操作失败，请稍后重试',
				error: process.env.NODE_ENV === 'development' ? error.message : undefined
			};
		}
	},

	//评论信息
	async collect(params) {
		try {
			const {
				sendUserId,
				publicMsgId,
				type
			} = params;
			// 1. 参数校验
			if (!publicMsgId || !isvalidObjectId(publicMsgId)) {
				return {
					code: false,
					msg: '无效的公共消息ID'
				};
			}
			if (!['add', 'del'].includes(type)) {
				return {
					code: false,
					msg: `不支持的操作类型：${type}，仅支持 add/del`
				};
			}
			// 新增：校验用户ID有效性
			console.log("Id类型", typeof sendUserId);
			if (!sendUserId || typeof sendUserId !== 'string') {
				return {
					code: false,
					msg: '用户ID无效'
				};
			}

			const collection = db.collection('message_public');

			// 2. 收藏操作（用 addToSet 自动去重）
			if (type === 'add') {
				const updateRes = await collection.where({
					_id: publicMsgId,
					// 修正：判断数组collect中不包含sendUserId
					collect: _.not(_.in([sendUserId]))
				}).update({
					collect: _.addToSet(sendUserId), // 向数组添加元素（去重）
					updateTime: Date.now()
				});
				console.log('添加后', updateRes);
				if (updateRes.updated > 0) {
					return {
						code: true,
						msg: '收藏成功'
					};
				} else {
					// 校验是否已收藏
					const msgRes = await collection.where({
						_id: publicMsgId
					}).field({
						collect: 1
					}).get();
					console.log("是否收藏？", msgRes);

					const isCollected = msgRes.data[0]?.collect?.includes(sendUserId) || false;
					return {
						code: false,
						msg: isCollected ? '已收藏该消息' : '收藏失败'
					};
				}
			}

			// 3. 取消收藏操作
			if (type === 'del') {
				const updateRes = await collection.where({
					_id: publicMsgId,
					// 修正：判断数组collect中包含sendUserId
					collect: _.in([sendUserId])
				}).update({
					collect: _.pull(sendUserId), // 从数组移除元素
					updateTime: Date.now()
				});

				if (updateRes.updated > 0) {
					return {
						code: true,
						msg: '取消收藏成功'
					};
				} else {
					return {
						code: false,
						msg: '未收藏该消息，无需取消'
					};
				}
			}

			return {
				code: false,
				msg: '操作类型错误'
			};
		} catch (error) {
			console.error('collect 执行错误：', error);
			return {
				code: false,
				msg: '收藏操作失败，请稍后重试',
				error: process.env.NODE_ENV === 'development' ? error.message : undefined
			};
		}
	},

	/**
	 * 用户收藏管理（增删）
	 * @param {Object} data - 参数对象
	 * @param {string} data.userId - 操作用户ID（自动获取）
	 * @param {string} data.action - 操作类型（add/remove）
	 * @param {string} [data.favorite_id] - 收藏ID（add时可选，自动生成）
	 * @param {string} data.type - 收藏类型（location/group/...）
	 * @param {string} data.name - 收藏名称
	 * @param {string} [data.address] - 收藏地址（location类型必传）
	 * @param {Object} data.location - 收藏位置（location类型必传）
	 * @returns {Object} 统一格式结果
	 */
	async favorite(data) {

		try {
			const {
				action,
				userId,
				type,
				name,
				location,
				address,
				favorite_id,
				config
			} = data;

			// 1. 基础参数校验（补充关键校验）
			if (!['add', 'remove'].includes(action)) {
				return {
					code: false,
					msg: `不支持的操作类型：${action}，仅支持 add/remove`
				};
			}
			if (!userId || typeof userId !== 'string') {
				return {
					code: false,
					msg: 'userId必须为有效字符串'
				};
			}
			if (action === 'add') {
				if (!name || typeof name !== 'string' || name.trim() === '') {
					return {
						code: false,
						msg: '收藏名称不能为空'
					};
				}
				if (type === 'location' && (!location || !isvalidLocation(location))) {
					return {
						code: false,
						msg: '无效的位置参数'
					};
				}
			}

			// 2. 添加收藏逻辑
			if (action === 'add') {
				// 生成当前格子编码
				const currentGridCode = linliUtils.getGridCode(location); // 假设该函数返回 "x,y" 格式
				// 计算周边5个格子的编码范围
				const surroundingGridCodes = linliUtils.getSurroundingGrids(currentGridCode, 5);

				// 关键：查询用户已收藏中是否有周边格子的记录
				const userRes = await db.collection('linli-user').where({
						userId
					})
					.field({
						favorite: 1,
						name: 1
					})
					.get();

				if (userRes.data.length === 0) {
					return {
						code: false,
						msg: '用户不存在'
					};
				}
				const userFavorites = userRes.data[0].favorite || [];

				// 2. 在本地判断是否有周边格子的收藏
				const hasSurrounding = userFavorites.some(fav =>
					surroundingGridCodes.includes(fav.favorite_id)
				);

				if (hasSurrounding) {
					// 找到第一个匹配的名称
					const existingName = userFavorites.find(fav =>
						surroundingGridCodes.includes(fav.favorite_id)
					).name;
					return {
						code: false,
						msg: `位置已在“${existingName}"中收藏：`,
						existingName
					};
				}

				// 构建收藏数据（新增gridCode字段）
				const addData = {
					favorite_id: favorite_id || currentGridCode, // 改用时间戳生成唯一ID
					type,
					name: name.trim(),
					address: type === 'location' ? address?.trim() || '' : '',
					location: location,
					createTime: Date.now(),
					config: config
				};

				// 开启事务：确保用户表和新表操作原子性
				const transaction = await db.startTransaction();
				try {
					// 2.1 更新用户收藏表（添加新收藏）
					const userUpdateRes = await transaction.collection('linli-user').where({
						userId
					}).update({
						favorite: _.push(addData),
						updateTime: Date.now()
					});

					// 2.2 同步存入新表（收藏点坐标及格子编码）
					await transaction.collection('opendb-poi').add({
						type,
						userId, // 关联用户
						grid: currentGridCode, // 格子编码
						location: location,
						updateTime: Date.now() // 原始坐标

					});

					// 提交事务
					await transaction.commit();

					// 返回更新后的用户收藏数据
					const userData = await getUserFavorites(userId); // 复用之前的抽取函数
					return {
						code: true,
						msg: '添加收藏成功',
						data: userData
					};
				} catch (error) {
					// 事务回滚（避免部分操作成功）
					await transaction.rollback();
					throw error;
				}
			}

			// 3. 删除收藏逻辑（同步删除新表数据）
			if (action === 'remove') {
				if (!favorite_id || typeof favorite_id !== 'string') {
					return {
						code: false,
						msg: '需传入有效的收藏ID'
					};
				}

				// 查询用户收藏中对应的记录（获取gridCode用于删除新表）
				const userRes = await db.collection('linli-user').where({
					userId
				}).field({
					favorite: 1
				}).get();
				if (userRes.data.length === 0) {
					return {
						code: false,
						msg: '用户不存在'
					};
				}
				const originalFavorites = userRes.data[0].favorite || [];
				const targetFavorite = originalFavorites.find(item => item.favorite_id === favorite_id);
				if (!targetFavorite) {
					return {
						code: false,
						msg: '该收藏不存在'
					};
				}

				// 开启事务：同步删除用户表和新表
				const transaction = await db.startTransaction();
				try {
					// 3.1 更新用户表（移除收藏）
					await transaction.collection('linli-user').where({
						userId
					}).update({
						favorite: originalFavorites.filter(item => item.favorite_id !== favorite_id),
						updateTime: Date.now()
					});

					// 3.2 删除新表中对应的收藏点记录
					await transaction.collection('opendb-poi').where({
						userId,
						grid: favorite_id
					}).remove();

					await transaction.commit();

					// 返回更新后的数据
					const userData = await getUserFavorites(userId);
					return {
						code: true,
						msg: '删除收藏成功',
						data: userData
					};
				} catch (error) {
					await transaction.rollback();
					throw error;
				}
			}

			return {
				code: false,
				msg: '操作类型错误'
			};
		} catch (error) {
			console.error('favorite 执行错误：', error);
			return {
				code: false,
				msg: '收藏操作失败，请稍后重试',
				error: process.env.NODE_ENV === 'development' ? error.message : undefined
			};
		}
	},
	/**
	 * 批量删除云存储文件
	 * @param {Array} fileIDs - 云文件ID数组（['cloud://xxx', 'cloud://yyy']）
	 * @returns {Object} 统一格式结果
	 */
	async deleFile(fileIDs) {
		try {
			if (!Array.isArray(fileIDs) || fileIDs.length === 0) {
				return {
					code: false,
					msg: '需传入有效的文件ID数组'
				};
			}

			// 过滤无效文件ID
			const validFileIDs = fileIDs.filter(id => typeof id === 'string' && id.startsWith('cloud://'));
			if (validFileIDs.length === 0) {
				return {
					code: false,
					msg: '无有效文件ID可删除'
				};
			}

			const deleteRes = await uniCloud.deleteFile({
				fileList: validFileIDs
			});

			// 处理删除结果（区分成功/失败）
			const successCount = deleteRes.successFileList?.length || 0;
			const failCount = deleteRes.failedFileList?.length || 0;

			return {
				code: true,
				msg: `文件删除完成：成功${successCount}个，失败${failCount}个`,
				data: {
					successFileList: deleteRes.successFileList || [],
					failedFileList: deleteRes.failedFileList || []
				}
			};
		} catch (error) {
			console.error('deleFile 执行错误：', error);
			return {
				code: false,
				msg: '文件删除失败，请稍后重试',
				error: process.env.NODE_ENV === 'development' ? error.message : undefined
			};
		}
	},

	/**
	 * 根据地理区域查询POI（opendb-poi集合）
	 * @param {Object} Region - 区域范围 { southwest: {lon, lat}, northeast: {lon, lat} }
	 * @returns {Object} 统一格式结果
	 */
	async getUsers(Region) {
		try {
			if (!Region || typeof Region !== 'object' || !Region.southwest || !Region.northeast) {
				return {
					code: false,
					msg: '需传入有效的区域范围（southwest + northeast）'
				};
			}

			const {
				southwest,
				northeast
			} = Region;
			// 校验区域经纬度
			if (!isvalidLocation(southwest) || !isvalidLocation(northeast)) {
				return {
					code: false,
					msg: '区域经纬度无效'
				};
			}

			// 构建GeoJSON多边形（矩形区域：西南→东北→西北→东南→西南）
			const polygon = [
				[southwest.longitude, southwest.latitude],
				[northeast.longitude, southwest.latitude],
				[northeast.longitude, northeast.latitude],
				[southwest.longitude, northeast.latitude],
				[southwest.longitude, southwest.latitude]
			];

			// 地理空间查询（需先给 opendb-poi 集合的 location 字段创建地理索引）
			const queryRes = await db.collection('opendb-poi').where({
				location: _.geoWithin({
					geometry: {
						type: 'Polygon',
						coordinates: [polygon]
					}
				})
			}).field({
				_id: 1,
				name: 1,
				location: 1,
				address: 1,
				category: 1
			}).get();

			return {
				code: true,
				msg: `查询到${queryRes.data.length}个POI`,
				data: queryRes.data || []
			};
		} catch (error) {
			console.error('getUsers 执行错误：', error);
			return {
				code: false,
				msg: 'POI查询失败，请稍后重试',
				error: process.env.NODE_ENV === 'development' ? error.message : undefined
			};
		}
	},
	//获取收藏未读数及最后一条记录
	/**
	 * @param {Object} user 用户
	 * @param {Object} lastTimeMap 最后时间映射
	 */
	async getFavoritesInfo(user, lastTimeMap) {
		// 存储结果：key为收藏ID，value为该收藏的信息
		const result = {};

		// 校验：用户无收藏时直接返回空对象
		if (!user?.favorite || !Array.isArray(user.favorite) || user.favorite.length === 0) {
			return result;
		}

		// 遍历每个收藏项，单独计算其相关数据
		for (const favoriteItem of user.favorite) {
			const {
				favorite_id,
				location,
				name
			} = favoriteItem;
			// 跳过缺少关键信息的收藏项
			if (!favorite_id || !location || !name) continue;

			// 1. 生成当前收藏点的网格及周边5半径内的网格（去重）
			const gridMap = new Map();
			const centerGrid = linliUtils.getGridCode(location); // 当前收藏点的网格
			const surroundingGrids = linliUtils.linliUtils.getSurroundingGrids(centerGrid, 5); // 周边5半径网格
			// 合并中心网格和周边网格，去重
			[centerGrid, ...surroundingGrids].forEach(grid => {
				gridMap.set(grid, true);
			});
			const gridList = [...gridMap.keys()];
			if (gridList.length === 0) {
				// 无有效网格时，该收藏项信息置空
				result[favorite_id] = {
					name: null,
					unreadCount: 0,
					recentSpeakers: [],
					lastMsg: null,
					lastMsgTime: null
				};
				continue;
			}

			// 2. 未读信息数：时间戳 > 该收藏的最后阅读时间（从lastTimeMap获取）
			console.log("上传的最后时间", lastTimeMap);
			const lastReadTime = lastTimeMap?.[favorite_id] || 0; // 假设lastTimeMap是{收藏ID: 最后阅读时间戳}
			console.log("最后时间", lastReadTime);

			const unreadCondition = db.command.gt(lastReadTime);

			// 2.1 查询未读信息总数
			const unreadCountRes = await db.collection('message_public')
				.where({
					grid: db.command.in(gridList),
					timestamp: unreadCondition
				})
				.count();
			const unreadCount = unreadCountRes.total || 0;

			// 3. 最后一条信息及时间（包含已读和未读，取最新的）
			const lastMsgRes = await db.collection('message_public')
				.where({
					grid: db.command.in(gridList)
				})
				.orderBy('timestamp', 'desc') // 按时间倒序
				.limit(1)
				.get();




			// 5. 以收藏ID为键，存入结果
			result[favorite_id] = {
				name: favoriteItem.name,
				unreadCount, // 未读信息数
				lastMsgRes
			};
		}

		return result;
	},
	/**
	 * @param {Object} id
	 * @param {Object} config
	 * @returns {boolean}
	 */
	async setConfig(id, config) {
		try {
			const res = await db.collection('linli-user').where({
				userId: id
			}).update({
				stateConfig: config
			})
			if (res.updated > 0) {
				return true;
			} else {
				return false
			}



		} catch (e) {
			return false
		}
	},

	/**
	 * 查询用户信息
	 * @param {Object} id - 用户ID
	 * @param {Object|boolean} field - 查询的字段，为 true 时返回全部字段
	 * @returns {object} 成功 {code:true, data} 或 失败 {code:false, errMsg}
	 */
	async getUserInfo(id, field) {
		try {
			// 构建查询
			const query = db.collection('linli-user').where({
				userId: id
			});

			// 如果 field 为 true，则返回全部字段，不调用 .field()
			// 如果 field 是一个对象，则按对象指定的字段返回
			// 如果 field 未提供或为 falsy 值，则使用默认字段
			if (field === true) {
				// 返回全部字段，不做处理
			} else {
				const queryField = field || {
					nicName: 1,
					nicPic: 1
				};
				query.field(queryField);
			}

			const res = await query.get();

			if (res.data.length > 0) {
				// 直接返回查询到的整个用户对象
				return {
					code: true,
					data: res.data[0]
				}
			}

			return {
				code: false,
				errMsg: '查无此人'
			}
		} catch (e) {
			console.error(e);
			return {
				code: false,
				errMsg: e.message || '查询失败'
			}
		}
	},
	/**
	 * @param {Object} userId
	 * @param {Object} params
	 */
	async updataUser(userId, params) {


		try {
			const res =await db.collection('linli-user').where({
				userId: userId
			}).update(params)
			if (res.updated > 0) {
      
				return {
					code: true,
					errMsg: '更新成功'
				}
				
			} else {
				return {
					code: false,
					errMsg: '更新失败'
				}
			}


		} catch (e) {
			return {
				code: false,
				errMsg: e
			}

		}
	},



	/**
	 * 查询符合条件的数据（包含72小时限制）
	 * @param {Array} gridCodes - 格子编码列表
	 * @param {Object} gridTimeMap - 格子时间阈值映射
	 * @returns {Promise<Array>} 符合条件的数据
	 */
	async getPublicMsg(gridCodes, gridTimeMap) {
		// 参数校验
		if (!Array.isArray(gridCodes) || gridCodes.length === 0) {
			throw new Error('请传入有效的格子编码列表');
		}
		if (typeof gridTimeMap !== 'object' || gridTimeMap === null) {
			throw new Error('请传入有效的格子时间映射');
		}

		try {

			const collection = db.collection('message_public');
			const command = db.command;

			// 计算72小时前的时间戳（毫秒）
			const seventyTwoHoursAgo = Date.now() - 72 * 60 * 60 * 1000;

			const orConditions = gridCodes.map(gridCode => {
				const gridTimeThreshold = gridTimeMap[gridCode] ?? 0;
				return {
					grid: gridCode,
					// 时间戳需同时大于两个值：格子阈值 和 72小时前（取两者中较大的作为实际阈值）
					timestamp: command.gt(Math.max(gridTimeThreshold, seventyTwoHoursAgo))
				};
			});

			// 执行查询
			const res = await collection
				.where(command.or(...orConditions)) // 多格子条件OR连接
				.orderBy('timestamp', 'desc')
				.get();

			return {
				code: 0,
				data: res.data
			}
		} catch (error) {
			console.error('查询失败：', error);
			throw error;
		}
	},
	// 获取群消息
	async getGroupMsg(groupId, lastTime, skip = 0) {
		return queryMessages({
			collectionName: 'message_group',
			where: {
				'target.targetId': groupId,
				timestamp: _.gt(lastTime)
			},
			limit: 20, // 群消息每页100条
			skip
		});
	},

	// 获取个人消息
	async getPersonMsg(user, lastTime, skip = 0) {
		return queryMessages({
			collectionName: 'message_person',
			where: {
				'target.targetId': user.userId,
				timestamp: _.gt(lastTime)
			},
			limit: 20, // 个人消息每页100条
			skip
		});
	},



	/**
	 * 根据城市获取今日新闻
	 * @param {Object} location - 包含 latitude 和 longitude 的对象
	 */
	async doubao(location) {
		try {
			// 1. 将经纬度转换为城市地址
			const addressRes = await amap.location2address(location);
			if (!addressRes || !addressRes.cityAddress) {
				return {
					success: false,
					code: 400,
					msg: '无法根据地理位置获取城市信息'
				};
			}
			const city = addressRes.cityAddress;
			const cacheKey = `city_news:${city}`;
			const today = getTodayDate();

			// 2. 优先检查 Redis 缓存
			const cachedData = await redis.get(cacheKey);
			if (cachedData) {
				try {
					const parsedData = JSON.parse(cachedData);
					// 检查缓存的新闻是否是今天的
					if (parsedData.date === today) {
						console.log(`从缓存中获取 ${city} 的今日新闻`);
						return {
							success: true,
							data: parsedData.news
						};
					} else {
						console.log(`${city} 的缓存新闻不是今天的 (${parsedData.date})，将重新获取`);
					}
				} catch (e) {
					console.error('缓存数据格式错误，将重新获取:', e);
				}
			}

			// 3. 缓存未命中或已过期，检查数据库
			const dbRes = await db.collection('news').where({
				city
			}).get();
			if (dbRes.data && dbRes.data.length > 0) {
				const latestNews = dbRes.data[0]; // 假设一条城市记录只有一条最新新闻
				// 检查数据库中的新闻是否是今天的
				if (latestNews.date === today) {
					news = latestNews.news;
					console.log(`从数据库中获取 ${city} 的今日新闻`);

					// 更新缓存
					await redis.set(cacheKey, JSON.stringify({
						news,
						date: today
					}), 'EX', 86400);

					return {
						success: true,
						data: news
					};
				} else {
					console.log(`${city} 的数据库新闻不是今天的 (${latestNews.date})，将重新获取`);
				}
			}

			// 4. 数据库也未找到今日新闻，调用豆包 API 获取
			console.log(`缓存和数据库均未找到 ${city} 的今日新闻，调用豆包 API...`);
			const requestData = {
				"model": "doubao-seed-1-6-flash-250828",
				"messages": [{
					"role": "user",
					"content": `请提供 ${city} 近36小时的新闻。`
				}]
			};

			const apiRes = await uniCloud.httpclient.request(
				"https://ark.cn-beijing.volces.com/api/v3/chat/completions", {
					method: 'POST',
					headers: {
						'Content-Type': 'application/json',
						'Authorization': 'Bearer 395e9657-25b4-46d8-8ad4-1757c344cac5' // 注意：生产环境使用环境变量
					},
					data: requestData,
					timeout: 30000,
					dataType: 'json'
				}
			);

			if (apiRes.status !== 200 || !apiRes.data || !apiRes.data.choices) {
				throw new Error(`API 调用失败: ${apiRes.status} - ${JSON.stringify(apiRes.data)}`);
			}

			const news = apiRes.data.choices[0].message.content.trim();

			// 5. 将新闻存入 Redis 和数据库，并带上今天的日期
			const dataToStore = {
				news,
				date: today
			};
			await redis.set(cacheKey, JSON.stringify(dataToStore), 'EX', 86400);
			console.log(`豆包 API 获取 ${city} 今日新闻成功，并已缓存`);

			// 更新或插入数据库记录
			if (dbRes.data && dbRes.data.length > 0) {
				await db.collection('news').doc(dbRes.data[0]._id).update(dataToStore);
			} else {
				await db.collection('news').add({
					...dataToStore,
					city
				});
			}
			console.log(`豆包 API 获取 ${city} 今日新闻成功，并已存入数据库`);

			return {
				success: true,
				data: news
			};

		} catch (error) {
			console.error('获取城市今日新闻时发生错误:', error);
			return {
				success: false,
				code: 500,
				msg: `获取今日新闻失败，请稍后重试。`
			};
		}

	}
}

/**
 * 获取今天的日期，格式为 'YYYY-MM-DD'
 * @returns {string}
 */
function getTodayDate() {
	const now = new Date();
	const year = now.getFullYear();
	const month = String(now.getMonth() + 1).padStart(2, '0');
	const day = String(now.getDate()).padStart(2, '0');
	return `${year}-${month}-${day}`;
}
/**
 * 工具函数：射线法判断点是否在多边形内
 * @param {Object} point - 目标点 { longitude: number, latitude: number }
 * @param {Array} polygon - 多边形坐标 [[lon1, lat1], [lon2, lat2], ...]
 * @returns {boolean} 是否在内部
 */
function isPointInPolygon(point, polygon) {
	if (!isvalidLocation(point) || !Array.isArray(polygon) || polygon.length < 3) return false;

	let intersections = 0;
	const pointLon = point.longitude;
	const pointLat = point.latitude;

	for (let i = 0; i < polygon.length; i++) {
		const [lon1, lat1] = polygon[i];
		const [lon2, lat2] = polygon[(i + 1) % polygon.length];

		// 跳过无效边
		if (typeof lon1 !== 'number' || typeof lat1 !== 'number' || typeof lon2 !== 'number' ||
			typeof lat2 !==
			'number') {
			continue;
		}

		// 判断点的纬度是否在边的纬度范围内
		const latRangeMin = Math.min(lat1, lat2);
		const latRangeMax = Math.max(lat1, lat2);
		if (pointLat < latRangeMin || pointLat > latRangeMax) continue;

		// 计算边与射线的交点经度
		const lonIntersect = (pointLat - lat1) * (lon2 - lon1) / (lat2 - lat1) + lon1;
		if (pointLon <= lonIntersect) intersections++;
	}

	return intersections % 2 === 1;
}

/**
 * 创建注册用户信息
 * @param {string} userTel - 用户手机号
 * @returns {Promise<{code: number, data: object}|{code: number, err: Error}>}
 */
async function registUser(userTel, pushId) {
	try {

		const name_id = linliUtils.generateString(userTel);

		// 构建用户对象
		const user = {
			userId: name_id,
			userTel: userTel,
			nicName: name_id,
			stateConfig: {
				autoPlayVoice: true,
				receiveDistance: 500,
				isSound: 1,
				acceptStrangerMsg: true,
				exposePhone: false,
				exposeComment: true,
				joinCluster: false,
				radius: 5
			},
			push_clientid: pushId || '', // 防止cid不存在
			nicPic: {
				url: '',
				fileID: ''
			},
			registTime: Date.now(),
			friends: [],
			joinedGroups: [], // 改为更合适的命名
			gridRadius: 5,
			signature: '', // 统一为小写
			favorite: []
		};

		return {
			code: 0, // 成功
			data: user
		};
	} catch (e) {
		console.error('注册用户失败:', e);
		return {
			code: 1, // 明确失败状态码
			err: e
		};
	}
}
async function updatePoi(user, location) {
	console.log("更新所有isFly的项目", user, location);
	try {
		// 1. 严格校验参数
		if (!user || typeof user !== 'object' || !user.userId) {
			throw new Error('无效的 user 参数（缺少 userId）');
		}
		if (!location || !location.longitude || !location.latitude) {
			throw new Error('无效的 location 参数（缺少经纬度）');
		}

		// 2. 构建更新数据
		const newData = {
			grid: linliUtils.getGridCode(location),
			location: new db.Geo.Point(location.longitude, location.latitude),
			update_time: Date.now(),
			isFly: true
		};

		// 3. 执行更新（条件明确，仅更新当前用户的记录）
		const updata = await db.collection('opendb-poi')
			.where({
				userId: user.userId
			})
			.update(newData);
		console.log("POI更新情况", updata);

	} catch (error) {
		console.error('POI 操作失败：', error);
		throw error; // 抛出错误，让上层处理
	}
}

/**
 * 通用消息查询方法（封装重复的查询逻辑）
 * @param {Object} options - 查询参数
 * @param {string} options.collectionName - 集合名称（如 'message_public'）
 * @param {Object} options.where - 查询条件（where 子句）
 * @param {number} options.limit - 每页条数
 * @param {number} options.skip - 跳过条数
 * @param {string} [options.orderByField='timestamp'] - 排序字段
 * @param {string} [options.orderByDirection='desc'] - 排序方向（'desc'/'asc'）
 * @returns {Promise<Object>} 包含 code、count、data 的结果
 */

async function queryMessages(options) {
	try {
		const {
			collectionName,
			where,
			limit,
			skip,
			orderByField = 'timestamp',
			orderByDirection = 'desc'
		} = options;

		// 1. 计算总数
		const {
			total
		} = await db.collection(collectionName)
			.where(where)
			.count();

		if (total <= 0) {
			return {
				code: false,
				errMsg: '无记录'
			};
		}

		// 2. 查询数据
		const res = await db.collection(collectionName)
			.where(where)
			.orderBy(orderByField, orderByDirection)
			.limit(limit)
			.skip(skip)
			.get();

		if (res.data.length === 0) {
			return {
				code: false,
				errMsg: '无记录'
			};
		}

		return {
			code: true,
			count: total,
			data: res.data // 返回 Message 实例数组，而非原始数据
		};
	} catch (e) {
		console.error(`查询 ${options.collectionName} 失败:`, e);
		return {
			code: false,
			errMsg: '查询失败',
			error: e.message
		};
	}
}
async function getUserFavorites(userId) {
	const res = await db.collection('linli-user').where({
			userId
		})
		.field({
			favorite: 1,
			userId: 1
		})
		.get();
	return res.data[0] || {
		userId,
		favorite: []
	};
}